import { Injectable, Inject, Logger, HttpException, HttpStatus } from '@nestjs/common';

import { InjectRepository } from "@nestjs/typeorm";
import { Repository, Like } from "typeorm";
import { User } from "./entities/user.entity";
import { Role } from "./entities/role.entity";
import { Permission } from "./entities/permission.entity";

import { RegisterUserDto } from "./dto/register-user.dto";
import { LoginUserDto } from "./dto/login-user.dto";

import { LoginUserVo } from "./vo/login-user.vo";

import { RedisService } from "../redis/redis.service";

import { md5 } from "../utils";
import { UpdateUserPasswordDto } from './dto/update-user-password.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Injectable()
export class UserService {
    // 使用Logger类打印错误日志，供后端排查错误
    private logger = new Logger();

    @Inject(RedisService)
    private redisService: RedisService;

    @InjectRepository(User)
    private userRepository: Repository<User>;

    @InjectRepository(Role)
    private roleRepository: Repository<Role>;

    @InjectRepository(Permission)
    private permissionRepository: Repository<Permission>;

    /* 初始化一些数据 */
    async initData() {
        const user1 = new User();
        user1.username = "zhangsan";
        user1.password = md5("111111");
        user1.email = "xxx@xx.com";
        user1.isAdmin = true;
        user1.nickName = '张三';
        user1.phoneNumber = '13233323333';

        const user2 = new User();
        user2.username = 'lisi';
        user2.password = md5("222222");
        user2.email = "yy@yy.com";
        user2.nickName = '李四';

        const role1 = new Role();
        role1.name = '管理员';

        const role2 = new Role();
        role2.name = '普通用户';

        const permission1 = new Permission();
        permission1.code = 'ccc';
        permission1.description = '访问 ccc 接口';

        const permission2 = new Permission();
        permission2.code = 'ddd';
        permission2.description = '访问 ddd 接口';

        user1.roles = [role1];
        user2.roles = [role2];

        role1.permissions = [permission1, permission2];
        role2.permissions = [permission1];

        // 注意保存顺序，被关联的表要先执行，否则顺序错的话，关联表中可能没有数据；
        await this.permissionRepository.save([permission1, permission2]);
        await this.roleRepository.save([role1, role2]);
        await this.userRepository.save([user1, user2]);
    }

    /* 注册逻辑 */
    async register(user: RegisterUserDto) {
        const { username, password, email, nickName } = user;

        // 1.判断邮箱验证码
        const captcha = await this.redisService.get(`captcha_${user.email}`);
        if(!captcha) throw new HttpException("验证码已失效", HttpStatus.BAD_REQUEST);
        if(user.captcha !== captcha) throw new HttpException("验证码不正确", HttpStatus.BAD_REQUEST);

        // 2.判断用户是否存在
        const foundUser = await this.userRepository.findOneBy({
            username
        });
        if(foundUser) throw new HttpException("用户已存在", HttpStatus.BAD_REQUEST);

        // 3.创建新用户
        const newUser = new User();
        newUser.username = username;
        newUser.password = md5(password);
        newUser.email = email;
        newUser.nickName = nickName;
        try {
            await this.userRepository.save(newUser);
            return "注册成功";
        }catch(error) {
            this.logger.error(error, UserService);
            return "注册失败";
        }
    }

    /* 登录逻辑 */
    async login(loginUserDto: LoginUserDto, isAdmin: boolean) {
        // 1.判断用户是否存在
        const user = await this.userRepository.findOne({
            where: {
                username: loginUserDto.username,
                isAdmin
            },
            relations: ['roles', 'roles.permissions']
        });

        if(!user) throw new HttpException("用户不存在", HttpStatus.BAD_REQUEST);
        // 2.判断密码是否正确
        if(user.password !== md5(loginUserDto.password)) throw new HttpException("密码错误", HttpStatus.BAD_REQUEST);

        // 3.使用VO封装的返回格式
        const vo = new LoginUserVo();
        vo.userInfo = {
            id: user.id,
            username: user.username,
            nickName: user.nickName,
            email: user.email,
            phoneNumber: user.phoneNumber,
            headPic: user.headPic,
            createTime: user.createTime,
            isFrozen: user.isFrozen,
            isAdmin: user.isAdmin,
            roles: user.roles.map(item => item.name),
            permissions: user.roles.reduce((arr, item) => {
                item.permissions.forEach(permission => {
                    if(arr.indexOf(permission) === -1) {
                        arr.push(permission);
                    }
                })

                return arr;
            }, [])
        }
        return vo;
    }

    /* 用户查找 */
    async findUserById(userId: number,  isAdmin: boolean) {
        const user = await this.userRepository.findOne({
            where: {
                id: userId,
                isAdmin
            },
            relations: ["roles", "roles.permissions"]
        });

        return {
            id: user.id,
            username: user.username,
            email: user.email,
            isAdmin: user.isAdmin,
            roles: user.roles.map(role => role.name),
            permissions: user.roles.reduce((arr, item) => {
                item.permissions.forEach(permission => {
                    if(arr.indexOf(permission) === -1) arr.push(permission)
                });
                return arr;
            }, [])
        }
    }

    /* 根据id获取用户详细信息 */
    async findUserDetailById(userId: number) {
        const user = await this.userRepository.findOne({
            where: {
                id: userId
            }
        });

        return user;
    }

    /* 更新密码逻辑 */
    async updatePassword(passwordDto: UpdateUserPasswordDto) {
        // 实际业务中更新密码是忘记密码中的业务，不需要登录，自然无法从登录信息中拿到用户id
        /* // 1.查询redis中是否有验证码信息
        const captcha = await this.redisService.get(`update_password_captcha_${passwordDto.email}`);
        if(!captcha) throw new HttpException("验证码已失效", HttpStatus.BAD_REQUEST);
        if(passwordDto.captcha !== captcha) throw new HttpException("验证码不正确", HttpStatus.BAD_REQUEST);    
        // 2.更新数据库中的密码
        const foundUser = await this.userRepository.findOneBy({
            id: userId
        });

        foundUser.password = md5(passwordDto.password);

        try {
            await this.userRepository.save(foundUser);
            return "密码修改成功";
        } catch(error) {
            this.logger.error(error, UserService);
            return "密码修改错误";
        } */

        const captcha = await this.redisService.get(`update_password_captcha_${passwordDto.email}`);
        if(!captcha) throw new HttpException("验证码已失效", HttpStatus.BAD_REQUEST);
        if(captcha !== passwordDto.captcha) throw new HttpException("验证码不正确", HttpStatus.BAD_REQUEST);
        const foundUser = await this.userRepository.findOneBy({
            username: passwordDto.username
        });
        if(!foundUser) throw new HttpException("用户不存在", HttpStatus.BAD_REQUEST);
        if(foundUser.email !== passwordDto.email) throw new HttpException("邮箱不正确", HttpStatus.BAD_REQUEST);
        foundUser.password = md5(passwordDto.password);
        try {
            await this.userRepository.save(foundUser);
            return "密码修改成功";
        } catch(error) {
            this.logger.error(error, UserService);
            return "密码修改错误";
        }
    }

    /* 更新用户信息 */
    async update(userId: number, updateUserDto: UpdateUserDto) {
        const captcha = await this.redisService.get(`update_user_captcha_${updateUserDto.email}`);
        if(!captcha) throw new HttpException("验证码已失效", HttpStatus.BAD_REQUEST);
        if(captcha !== updateUserDto.captcha) throw new HttpException("验证码不正确", HttpStatus.BAD_REQUEST);

        const foundUser = await this.userRepository.findOneBy({
            id: userId
        });

        if(updateUserDto.nickName) foundUser.nickName = updateUserDto.nickName;
        if(updateUserDto.headPic) foundUser.headPic = updateUserDto.headPic;

        try {
            await this.userRepository.save(foundUser);
            return "用户信息修改成功";
        }catch(error) {
            this.logger.error(error, UserService);
            return "用户信息修改失败";
        }
    }
    

    /* 冻结用户 */
    async frozen(id: number) {
        const user = await this.userRepository.findOneBy({
            id
        });
        if(!user) throw new HttpException("用户不存在", HttpStatus.BAD_REQUEST);
        user.isFrozen = true;
        await this.userRepository.save(user);
    }

    /* 获取用户列表 */
    async findUsersByPage(pageNo: number, pageSize: number, nickName: string, username: string, email: string) {
        const startPos = (pageNo -1) * pageSize;

        const condition: Record<string, any> = {};
        if(username) condition.username = Like(`%${username}%`);
        if(nickName) condition.nickName = Like(`%${nickName}%`);
        if(email) condition.email = Like(`%${email}%`);
    
        const [users, totalCount] = await this.userRepository.findAndCount({
            select: ["id","username","nickName","email","phoneNumber","isFrozen","headPic","createTime"], // 通过select指定返回哪些信息（select * 与 select xx, xxx的区别）
            skip: startPos,
            take: pageSize,
            where: condition
        });

        return {
            users,
            totalCount
        }
    }
}
